#include "base/channel.h"
#include "base/eventloop.h"
#include "base/logfile.h"
#include "base/log.h"
#include <memory.h>
#include <atomic>

#include "base/logbuffer.h"
namespace net_tools::base{
namespace detail{
struct Buffer_main{
  Buffer_main()
    :buffer{},
     now_buffer_index_()
  {
  };
  char buffer[_C::SET_LOG_BUFFER_SIZE];
  int now_buffer_index_;
};
}; // namespace detail

Logbuffer::Logbuffer(Logfile* logfile,net_tools::Eventloop* eventloop)
  :logfile_(logfile),
   eventloop_(eventloop),
   fd_stream_(stdout),
   p_now_buffer_(new detail::Buffer_main()),
   p_flush_buffer_(new detail::Buffer_main()),
   is_swap_(false),
   mutex_()
{
};
Logbuffer::~Logbuffer(){
};
void Logbuffer::Set_fd(FILE* fd_stream){
  NT_LOG_INFO << "Change Log file" << NT_LOG_ENDL;
  new_fd_stream_ = fd_stream;
  need_change_file_ = true;
  Call_thread_to_flush();
};
size_t Logbuffer::Flush(){
  mutex_.Lock(); //锁入
  if(!is_swap_){ //没换过区
    detail::Buffer_main* temp = p_now_buffer_; //换区
    p_now_buffer_ = p_flush_buffer_;
    p_flush_buffer_ = temp;
    is_swap_ = true;
  }
  if(need_change_file_) [[unlikely]] {
    fd_stream_ = new_fd_stream_;
  }
  mutex_.Unlock();
  size_t write_len = p_flush_buffer_->now_buffer_index_;
  size_t flush_len = fwrite_unlocked(p_flush_buffer_->buffer,1,p_flush_buffer_->now_buffer_index_,fd_stream_);
  p_flush_buffer_->now_buffer_index_ -= flush_len;
  while(p_flush_buffer_->now_buffer_index_ > 0){
    flush_len = fwrite_unlocked(p_flush_buffer_->buffer,1,p_flush_buffer_->now_buffer_index_,fd_stream_);
    p_flush_buffer_->now_buffer_index_ -= flush_len;
    // bug here need jude error
  }
  is_swap_ = false;//don't need mutex 只有一个线程会flush
  fflush(fd_stream_);
  return write_len;
};
// 叫醒线程 要在锁内
void Logbuffer::Call_thread_to_flush(){
  eventloop_->Run_in_right_thread([this]{logfile_->Flush();});
};
void Logbuffer::Output(void* address,short int len){
  mutex_.Lock();
  detail::Buffer_main* current_buffer = p_now_buffer_;
  int index = current_buffer->now_buffer_index_;
  current_buffer->now_buffer_index_ += len;
  if(current_buffer->now_buffer_index_ > _C::SET_LOG_HIGHLEVEL) [[unlikely]] { //现在这个区满了
    if(is_swap_) [[unlikely]] { //已经换区 无法换区
      current_buffer->now_buffer_index_-= len;
      mutex_.Unlock();
      return;
    }else{ //该区满了 则先把信息写入该区 然后换区 刷入
      memcpy(current_buffer->buffer+index,address,len);
      detail::Buffer_main* temp = current_buffer; //换区
      current_buffer = p_flush_buffer_;
      p_flush_buffer_ = temp;
      is_swap_ = true;
      Call_thread_to_flush();
      mutex_.Unlock();
      return;
    }
  }
  mutex_.Unlock();
  memcpy(current_buffer->buffer+index,address,len);
};

} // namespace net_tools::base